<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" lang="" xml:lang="">
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes" />
    <meta name="description" content="Interested in adding textures, lighting, shadows, normal maps, glowing objects, ambient occlusion, reflections, refractions, and more to your 3D game? Great! 3D Game Shaders For Beginners is a collection of shading techniques that will take your game visuals to new heights." />
    <meta property="og:title" content="Screen Space Refraction | 3D Game Shaders For Beginners" />
    <meta property="og:description" content="Interested in adding textures, lighting, shadows, normal maps, glowing objects, ambient occlusion, reflections, refractions, and more to your 3D game? Great! 3D Game Shaders For Beginners is a collection of shading techniques that will take your game visuals to new heights." />
    <meta property="og:image" content="https://i.imgur.com/JIDwVTm.png" />
    <meta name="twitter:title" content="Screen Space Refraction | 3D Game Shaders For Beginners" />
    <meta name="twitter:description" content="Interested in adding textures, lighting, shadows, normal maps, glowing objects, ambient occlusion, reflections, refractions, and more to your 3D game? Great! 3D Game Shaders For Beginners is a collection of shading techniques that will take your game visuals to new heights." />
    <meta name="twitter:image" content="https://i.imgur.com/JIDwVTm.png" />
    <meta name="twitter:card" content="summary_large_image" />
    <link rel="icon" type="image/x-icon" href="favicon.ico" />
    <meta name="author" content="David Lettier" />
    <title>Screen Space Refraction | 3D Game Shaders For Beginners</title>
    <style>
      code{white-space: pre-wrap;}
      span.smallcaps{font-variant: small-caps;}
      span.underline{text-decoration: underline;}
      div.column{display: inline-block; vertical-align: top; width: 50%;}
    </style>
    <style>
      code.sourceCode > span { display: inline-block; line-height: 1.25; }
      code.sourceCode > span { color: inherit; text-decoration: inherit; }
      code.sourceCode > span:empty { height: 1.2em; }
      .sourceCode { overflow: visible; }
      code.sourceCode { white-space: pre; position: relative; }
      div.sourceCode { margin: 1em 0; }
      pre.sourceCode { margin: 0; }
      @media screen {
      div.sourceCode { overflow: auto; }
      }
      @media print {
      code.sourceCode { white-space: pre-wrap; }
      code.sourceCode > span { text-indent: -5em; padding-left: 5em; }
      }
      pre.numberSource code
        { counter-reset: source-line 0; }
      pre.numberSource code > span
        { position: relative; left: -4em; counter-increment: source-line; }
      pre.numberSource code > span > a:first-child::before
        { content: counter(source-line);
          position: relative; left: -1em; text-align: right; vertical-align: baseline;
          border: none; display: inline-block;
          -webkit-touch-callout: none; -webkit-user-select: none;
          -khtml-user-select: none; -moz-user-select: none;
          -ms-user-select: none; user-select: none;
          padding: 0 4px; width: 4em;
          background-color: #232629;
          color: #7a7c7d;
        }
      pre.numberSource { margin-left: 3em; border-left: 1px solid #7a7c7d;  padding-left: 4px; }
      div.sourceCode
        { color: #cfcfc2; background-color: #232629; }
      @media screen {
      code.sourceCode > span > a:first-child::before { text-decoration: underline; }
      }
      code span. { color: #cfcfc2; } /* Normal */
      code span.al { color: #95da4c; } /* Alert */
      code span.an { color: #3f8058; } /* Annotation */
      code span.at { color: #2980b9; } /* Attribute */
      code span.bn { color: #f67400; } /* BaseN */
      code span.bu { color: #7f8c8d; } /* BuiltIn */
      code span.cf { color: #fdbc4b; } /* ControlFlow */
      code span.ch { color: #3daee9; } /* Char */
      code span.cn { color: #27aeae; } /* Constant */
      code span.co { color: #7a7c7d; } /* Comment */
      code span.cv { color: #7f8c8d; } /* CommentVar */
      code span.do { color: #a43340; } /* Documentation */
      code span.dt { color: #2980b9; } /* DataType */
      code span.dv { color: #f67400; } /* DecVal */
      code span.er { color: #da4453; } /* Error */
      code span.ex { color: #0099ff; } /* Extension */
      code span.fl { color: #f67400; } /* Float */
      code span.fu { color: #8e44ad; } /* Function */
      code span.im { color: #27ae60; } /* Import */
      code span.in { color: #c45b00; } /* Information */
      code span.kw { color: #cfcfc2; } /* Keyword */
      code span.op { color: #cfcfc2; } /* Operator */
      code span.ot { color: #27ae60; } /* Other */
      code span.pp { color: #27ae60; } /* Preprocessor */
      code span.re { color: #2980b9; } /* RegionMarker */
      code span.sc { color: #3daee9; } /* SpecialChar */
      code span.ss { color: #da4453; } /* SpecialString */
      code span.st { color: #f44f4f; } /* String */
      code span.va { color: #27aeae; } /* Variable */
      code span.vs { color: #da4453; } /* VerbatimString */
      code span.wa { color: #da4453; } /* Warning */
    </style>
    <!--[if lt IE 9]>
      <script src="//cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7.3/html5shiv-printshiv.min.js"></script>
    <![endif]-->
    <link rel="stylesheet" href="style.css" />
  </head>
  <body>
<p><a href="screen-space-reflection.html"><span class="emoji" data-emoji="arrow_backward">◀️</span></a> <a href="index.html"><span class="emoji" data-emoji="arrow_double_up">⏫</span></a> <a href="#"><span class="emoji" data-emoji="arrow_up_small">🔼</span></a> <a href="#copyright"><span class="emoji" data-emoji="arrow_down_small">🔽</span></a> <a href="foam.html"><span class="emoji" data-emoji="arrow_forward">▶️</span></a></p>
<h1 id="3d-game-shaders-for-beginners">3D Game Shaders For Beginners</h1>
<h2 id="screen-space-refraction-ssr">Screen Space Refraction (SSR)</h2>
<p align="center">
<img src="https://i.imgur.com/8Mdcn4y.gif" alt="Screen Space Refraction" title="Screen Space Refraction">
</p>

<p>Screen space refraction, much like <a href="screen-space-reflection.html">screen space reflection</a>, adds a touch of realism you can't find anywhere else. Glass, plastic, water, and other transparent/translucent materials spring to life.</p>
<p><a href="screen-space-reflection.html">Screen space reflection</a> and screen space refraction work almost identically expect for one major difference. Instead of using the reflected vector, screen space refraction uses the <a href="http://asawicki.info/news_1301_reflect_and_refract_functions.html">refracted vector</a>. It's a slight change in code but a big difference visually.</p>
<h3 id="vertex-positions">Vertex Positions</h3>
<p>Like SSAO, you'll need the vertex positions in view space. Referrer back to <a href="ssao.html#vertex-positions">SSAO</a> for details.</p>
<p>However, unlike SSAO, you'll need the scene's vertex positions with and without the refractive objects. Refractive surfaces are translucent, meaning you can see through them. Since you can see through them, you'll need the vertex positions behind the refractive surface. Having both the foreground and background vertex positions will allow you to calculate UV coordinates and depth.</p>
<h3 id="vertex-normals">Vertex Normals</h3>
<p>To compute the refractions, you'll need the scene's foreground vertex normals in view space. The background vertex normals aren't needed unless you need to incorporate the background surface detail while calculating the refracted UV coordinates and distances. Referrer back to <a href="ssao.html#vertex-normals">SSAO</a> for details.</p>
<p align="center">
<img src="https://i.imgur.com/MZ2R8I6.gif" alt="Normal maps versus no normal maps." title="Normal maps versus no normal maps.">
</p>

<p>Here you see the water refracting the light with and without normal maps. If available, be sure to use the normal mapped normals instead of the vertex normals. The smoother and flatter the surface, the harder it is to tell the light is being refracted. There will be some distortion but not enough to make it worthwhile.</p>
<p>To use the normal maps instead, you'll need to transform the normal mapped normals from tangent space to view space just like you did in the <a href="normal-mapping.html#fragment">lighting</a> calculations. You can see this being done in <a href="https://github.com/lettier/3d-game-shaders-for-beginners/blob/master/demonstration/shaders/fragment/normal.frag" target="_blank" rel="noopener noreferrer">normal.frag</a>.</p>
<h3 id="position-transformations">Position Transformations</h3>
<p align="center">
<img src="https://i.imgur.com/bXtXDyu.gif" alt="Position Transformations" title="Position Transformations">
</p>

<p>Just like <a href="ssao.html">SSAO</a> and <a href="screen-space-reflection.html">screen space reflection</a>, screen space refraction goes back and forth between the screen and view space. You'll need the camera lens' projection matrix to transform points in view space to clip space. From clip space, you'll have to transform the points again to UV space. Once in UV space, you can sample a vertex/fragment position from the scene which will be the closest position in the scene to your sample. This is the <em>screen space</em> part in <em>screen space refraction</em> since the "screen" is a texture UV mapped over a screen shaped rectangle.</p>
<h3 id="refracted-uv-coordinates">Refracted UV Coordinates</h3>
<p>Recall that UV coordinates range from zero to one for both U and V. The screen is just a 2D texture UV mapped over a screen-sized rectangle. Knowing this, the example code doesn't actually need the final rendering of the scene to compute the refraction. It can instead calculate what UV coordinate each screen pixel will eventually use. These calculated UV coordinates can be saved to a framebuffer texture and used later when the scene has been rendered.</p>
<p>The process of refracting the UV coordinates is very similar to the process of <a href="screen-space-reflection.html#reflected-uv-coordinates">reflecting the UV coordinates</a>. Below are the adjustments you'll need to turn reflection into refraction.</p>
<div class="sourceCode" id="cb1"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb1-1"><a href="#cb1-1"></a><span class="co">// ...</span></span>
<span id="cb1-2"><a href="#cb1-2"></a></span>
<span id="cb1-3"><a href="#cb1-3"></a>uniform sampler2D positionFromTexture;</span>
<span id="cb1-4"><a href="#cb1-4"></a>uniform sampler2D positionToTexture;</span>
<span id="cb1-5"><a href="#cb1-5"></a>uniform sampler2D normalFromTexture;</span>
<span id="cb1-6"><a href="#cb1-6"></a></span>
<span id="cb1-7"><a href="#cb1-7"></a><span class="co">// ...</span></span></code></pre></div>
<p>Reflection only deals with what is in front of the reflective surface. Refraction, however, deals with what is behind the refractive surface. To accommodate this, you'll need both the vertex positions of the scene with the refracting surfaces taken out and the vertex positions of the scene with the refracting surfaces left in.</p>
<p align="center">
<img src="https://i.imgur.com/FjQtjsm.gif" alt="Without and with refractive surfaces." title="Without and with refractive surfaces.">
</p>

<p><code>positionFromTexture</code> are the scene's vertex positions with the refracting surfaces left in. <code>positionToTexture</code> are the scene's vertex positions with the refracting surfaces taken out. <code>normalFromTexture</code> are the scene's vertex normals with the refraction surfaces left in. There's no need for the vertex normals behind the refractive surfaces unless you want to incorporate the surface detail for the background geometry.</p>
<div class="sourceCode" id="cb2"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb2-1"><a href="#cb2-1"></a><span class="co">// ...</span></span>
<span id="cb2-2"><a href="#cb2-2"></a></span>
<span id="cb2-3"><a href="#cb2-3"></a>uniform vec2 rior;</span>
<span id="cb2-4"><a href="#cb2-4"></a></span>
<span id="cb2-5"><a href="#cb2-5"></a><span class="co">// ...</span></span></code></pre></div>
<p>Refraction has one more adjustable parameter than reflection. <code>rior</code> is the relative index of refraction or relative refractive index. It is the ratio of the refraction indexes of two mediums. So for example, going from water to air is <code>1 / 1.33 ≈ 0.75</code>. The numerator is the refractive index of the medium the light is leaving and the denominator is the refractive index of the medium the light is entering. An <code>rior</code> of one means the light passes right through without being refracted or bent. As <code>rior</code> grows, the refraction will become a <a href="https://en.wikipedia.org/wiki/Total_internal_reflection">reflection</a>.</p>
<p>There's no requirement that <code>rior</code> must adhere to the real world. The demo uses <code>1.05</code>. This is completely unrealistic (light does not travel faster through water than air) but the realistic setting produced too many artifacts. In the end, the distortion only has to be believable—not realistic.</p>
<p align="center">
<img src="https://i.imgur.com/dDOnobK.gif" alt="Adjusting the relative index of refraction." title="Adjusting the relative index of refraction.">
</p>

<p><code>rior</code> values above one tend to elongate the refraction while numbers below one tend to shrink the refraction.</p>
<p>As it was with screen space reflection, the screen doesn't have the entire geometry of the scene. A refracted ray may march through the screen space and never hit a captured surface. Or it may hit a surface but it's the backside not captured by the camera. When this happened during reflection, the fragment was left blank. This indicated no reflection or not enough information to determine a reflection. Leaving the fragment blank was fine for reflection since the reflective surface would fill in the gaps.</p>
<p align="center">
<img src="https://i.imgur.com/vcQDAYU.gif" alt="Refraction Holes" title="Refraction Holes">
</p>

<p>For refraction, however, we must set the fragment to some UV. If the fragment is left blank, the refractive surface will contain holes that let the detail behind it come through. This would be okay for a completely transparent surface but usually the refractive surface will have some tint to it, reflection, etc.</p>
<div class="sourceCode" id="cb3"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb3-1"><a href="#cb3-1"></a>  <span class="co">// ...</span></span>
<span id="cb3-2"><a href="#cb3-2"></a></span>
<span id="cb3-3"><a href="#cb3-3"></a>  vec2 texSize  = textureSize(positionFromTexture, <span class="dv">0</span>).xy;</span>
<span id="cb3-4"><a href="#cb3-4"></a>  vec2 texCoord = gl_FragCoord.xy / texSize;</span>
<span id="cb3-5"><a href="#cb3-5"></a></span>
<span id="cb3-6"><a href="#cb3-6"></a>  vec4 uv = vec4(texCoord.xy, <span class="dv">1</span>, <span class="dv">1</span>);</span>
<span id="cb3-7"><a href="#cb3-7"></a></span>
<span id="cb3-8"><a href="#cb3-8"></a>  <span class="co">// ...</span></span></code></pre></div>
<p>The best choice is to select the UV as if the <code>rior</code> was one. This will leave the UV coordinate unchanged, allowing the background to show through instead of there being a hole in the refractive surface.</p>
<p align="center">
<img src="https://i.imgur.com/9fybLUO.png" alt="Refraction UV Map" title="Refraction UV Map">
</p>

<p>Here you see the refracted UV texture for the mill scene. The wheel and waterway disturb what is otherwise a smooth gradient. The disruptions shift the UV coordinates from their screen position to their refracted screen position.</p>
<div class="sourceCode" id="cb4"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb4-1"><a href="#cb4-1"></a>  <span class="co">// ...</span></span>
<span id="cb4-2"><a href="#cb4-2"></a></span>
<span id="cb4-3"><a href="#cb4-3"></a>  vec3 unitPositionFrom = normalize(positionFrom.xyz);</span>
<span id="cb4-4"><a href="#cb4-4"></a>  vec3 normalFrom       = normalize(texture(normalFromTexture, texCoord).xyz);</span>
<span id="cb4-5"><a href="#cb4-5"></a>  vec3 pivot            = normalize(refract(unitPositionFrom, normalFrom, rior.x));</span>
<span id="cb4-6"><a href="#cb4-6"></a></span>
<span id="cb4-7"><a href="#cb4-7"></a>  <span class="co">// ...</span></span></code></pre></div>
<p>The most important difference is the calculation of the refracted vector versus the reflected vector. Both use the unit position and normal but <code>refract</code> takes an additional parameter specifying the relative refractive index.</p>
<div class="sourceCode" id="cb5"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb5-1"><a href="#cb5-1"></a>    <span class="co">// ...</span></span>
<span id="cb5-2"><a href="#cb5-2"></a></span>
<span id="cb5-3"><a href="#cb5-3"></a>    frag      += increment;</span>
<span id="cb5-4"><a href="#cb5-4"></a>    uv.xy      = frag / texSize;</span>
<span id="cb5-5"><a href="#cb5-5"></a>    positionTo = texture(positionToTexture, uv.xy);</span>
<span id="cb5-6"><a href="#cb5-6"></a></span>
<span id="cb5-7"><a href="#cb5-7"></a>    <span class="co">// ...</span></span></code></pre></div>
<p>The <code>positionTo</code>, sampled by the <code>uv</code> coordinates, uses the <code>positionToTexture</code>. For reflection, you only need one framebuffer texture containing the scene's interpolated vertex positions in view space. However, for refraction, <code>positionToTexture</code> contains the vertex positions of the scene minus the refractive surfaces since the refraction ray typically goes behind the surface. If <code>positionFromTexture</code> and <code>positionToTexture</code> were the same for refraction, the refracted ray would hit the refractive surface instead of what is behind it.</p>
<h3 id="refraction-mask">Refraction Mask</h3>
<p align="center">
<img src="https://i.imgur.com/iuFYVWB.gif" alt="Material Specular" title="Material Specular">
</p>

<p>You'll need a mask to filter out the non-refractive parts of the scene. This mask will determine which fragment does and does not receive a refracted color. You could use this mask during the refracted UV calculation step or later when you actually sample the colors at the refracted UV coordinates.</p>
<p>The mill scene uses the models' material specular as a mask. For the demo's purposes, the specular map is sufficient but you may want to use a more specialized map. Refer back to <a href="screen-space-reflection.html#specular-map">screen space reflection</a> for how to render the specular map.</p>
<h2 id="background-colors">Background Colors</h2>
<p align="center">
<img src="https://i.imgur.com/AmT9RrU.gif" alt="Background Colors" title="Background Colors">
</p>

<p>You'll need to render the parts of the scene behind the refractive objects. This can be done by hiding the refractive objects and then rendering the scene to a framebuffer texture.</p>
<h3 id="foreground-colors">Foreground Colors</h3>
<p align="center">
<img src="https://i.imgur.com/6RPHULr.gif" alt="Foreground Colors" title="Foreground Colors">
</p>

<div class="sourceCode" id="cb6"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb6-1"><a href="#cb6-1"></a><span class="co">// ...</span></span>
<span id="cb6-2"><a href="#cb6-2"></a></span>
<span id="cb6-3"><a href="#cb6-3"></a>uniform sampler2D uvTexture;</span>
<span id="cb6-4"><a href="#cb6-4"></a>uniform sampler2D refractionMaskTexture;</span>
<span id="cb6-5"><a href="#cb6-5"></a>uniform sampler2D positionFromTexture;</span>
<span id="cb6-6"><a href="#cb6-6"></a>uniform sampler2D positionToTexture;</span>
<span id="cb6-7"><a href="#cb6-7"></a>uniform sampler2D backgroundColorTexture;</span>
<span id="cb6-8"><a href="#cb6-8"></a></span>
<span id="cb6-9"><a href="#cb6-9"></a><span class="co">// ...</span></span></code></pre></div>
<p>To render the actual refractions or foreground colors, you'll need the refracted UV coordinates, refraction mask, the foreground and background vertex positions, and the background colors.</p>
<div class="sourceCode" id="cb7"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb7-1"><a href="#cb7-1"></a>  <span class="co">// ...</span></span>
<span id="cb7-2"><a href="#cb7-2"></a></span>
<span id="cb7-3"><a href="#cb7-3"></a>  vec3  tintColor = vec3(<span class="fl">0.27</span>, <span class="fl">0.58</span>, <span class="fl">0.92</span>, <span class="fl">0.3</span>);</span>
<span id="cb7-4"><a href="#cb7-4"></a>  <span class="dt">float</span> depthMax  = <span class="dv">2</span>;</span>
<span id="cb7-5"><a href="#cb7-5"></a></span>
<span id="cb7-6"><a href="#cb7-6"></a>  <span class="co">// ...</span></span></code></pre></div>
<p><code>tintColor</code> and <code>depthMax</code> are adjustable parameters. <code>tintColor</code> colorizes the background color. <code>depthMax</code> ranges from zero to infinity. When the distance between the foreground and background position reaches <code>depthMax</code>, the foreground color will be the fully tinted background color. At distance zero, the foreground will be the background color.</p>
<div class="sourceCode" id="cb8"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb8-1"><a href="#cb8-1"></a>  <span class="co">// ...</span></span>
<span id="cb8-2"><a href="#cb8-2"></a></span>
<span id="cb8-3"><a href="#cb8-3"></a>  vec2 texSize  = textureSize(backgroundColorTexture, <span class="dv">0</span>).xy;</span>
<span id="cb8-4"><a href="#cb8-4"></a>  vec2 texCoord = gl_FragCoord.xy / texSize;</span>
<span id="cb8-5"><a href="#cb8-5"></a></span>
<span id="cb8-6"><a href="#cb8-6"></a>  vec4 uv              = texture(uvTexture,              texCoord);</span>
<span id="cb8-7"><a href="#cb8-7"></a>  vec4 mask            = texture(maskTexture,            texCoord);</span>
<span id="cb8-8"><a href="#cb8-8"></a>  vec4 positionFrom    = texture(positionFromTexture,    texCoord);</span>
<span id="cb8-9"><a href="#cb8-9"></a>  vec4 positionTo      = texture(positionToTexture,      uv.xy);</span>
<span id="cb8-10"><a href="#cb8-10"></a>  vec4 backgroundColor = texture(backgroundColorTexture, uv.xy);</span>
<span id="cb8-11"><a href="#cb8-11"></a></span>
<span id="cb8-12"><a href="#cb8-12"></a>  <span class="cf">if</span> (refractionMask.r &lt;= <span class="dv">0</span>) { fragColor = vec4(<span class="dv">0</span>); <span class="cf">return</span>; }</span>
<span id="cb8-13"><a href="#cb8-13"></a></span>
<span id="cb8-14"><a href="#cb8-14"></a>  <span class="co">// ...</span></span></code></pre></div>
<p>Pull out the uv coordinates, mask, background position, foreground position, and the background color.</p>
<p>If the refraction mask is turned off for this fragment, return nothing.</p>
<div class="sourceCode" id="cb9"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb9-1"><a href="#cb9-1"></a>  <span class="co">// ...</span></span>
<span id="cb9-2"><a href="#cb9-2"></a></span>
<span id="cb9-3"><a href="#cb9-3"></a>  <span class="dt">float</span> depth   = length(positionTo.xyz - positionFrom.xyz);</span>
<span id="cb9-4"><a href="#cb9-4"></a>  <span class="dt">float</span> mixture = clamp(depth / depthMax, <span class="dv">0</span>, <span class="dv">1</span>);</span>
<span id="cb9-5"><a href="#cb9-5"></a></span>
<span id="cb9-6"><a href="#cb9-6"></a>  vec3 shallowColor    = backgroundColor.rgb;</span>
<span id="cb9-7"><a href="#cb9-7"></a>  vec3 deepColor       = mix(shallowColor, tintColor.rgb, tintColor.a);</span>
<span id="cb9-8"><a href="#cb9-8"></a>  vec3 foregroundColor = mix(shallowColor, deepColor,     mixture);</span>
<span id="cb9-9"><a href="#cb9-9"></a></span>
<span id="cb9-10"><a href="#cb9-10"></a>  <span class="co">// ...</span></span></code></pre></div>
<p align="center">
<img src="https://i.imgur.com/IEFKerB.gif" alt="Refraction Depth" title="Refraction Depth">
</p>

<p>Calculate the depth or distance between the foreground position and the background position. At zero depth, the foreground color will be the shallow color. At <code>depthMax</code>, the foreground color will be the deep color. The deep color is the background color tinted with <code>tintColor</code>.</p>
<div class="sourceCode" id="cb10"><pre class="sourceCode c"><code class="sourceCode c"><span id="cb10-1"><a href="#cb10-1"></a>  <span class="co">// ...</span></span>
<span id="cb10-2"><a href="#cb10-2"></a></span>
<span id="cb10-3"><a href="#cb10-3"></a>  fragColor = mix(vec4(<span class="dv">0</span>), vec4(foregroundColor, <span class="dv">1</span>), uv.b);</span>
<span id="cb10-4"><a href="#cb10-4"></a></span>
<span id="cb10-5"><a href="#cb10-5"></a>  <span class="co">// ...</span></span></code></pre></div>
<p>Recall that the blue channel, in the refracted UV texture, is set to the visibility. The visibility declines as the refracted ray points back at the camera. While the visibility should always be one, it is put here for completeness. As the visibility lessens, the fragment color will receive less and less of the foreground color.</p>
<h3 id="source">Source</h3>
<ul>
<li><a href="https://github.com/lettier/3d-game-shaders-for-beginners/blob/master/demonstration/src/main.cxx" target="_blank" rel="noopener noreferrer">main.cxx</a></li>
<li><a href="https://github.com/lettier/3d-game-shaders-for-beginners/blob/master/demonstration/shaders/vertex/base.vert" target="_blank" rel="noopener noreferrer">base.vert</a></li>
<li><a href="https://github.com/lettier/3d-game-shaders-for-beginners/blob/master/demonstration/shaders/vertex/basic.vert" target="_blank" rel="noopener noreferrer">basic.vert</a></li>
<li><a href="https://github.com/lettier/3d-game-shaders-for-beginners/blob/master/demonstration/shaders/fragment/position.frag" target="_blank" rel="noopener noreferrer">position.frag</a></li>
<li><a href="https://github.com/lettier/3d-game-shaders-for-beginners/blob/master/demonstration/shaders/fragment/normal.frag" target="_blank" rel="noopener noreferrer">normal.frag</a></li>
<li><a href="https://github.com/lettier/3d-game-shaders-for-beginners/blob/master/demonstration/shaders/fragment/material-specular.frag" target="_blank" rel="noopener noreferrer">material-specular.frag</a></li>
<li><a href="https://github.com/lettier/3d-game-shaders-for-beginners/blob/master/demonstration/shaders/fragment/screen-space-refraction.frag" target="_blank" rel="noopener noreferrer">screen-space-refraction.frag</a></li>
<li><a href="https://github.com/lettier/3d-game-shaders-for-beginners/blob/master/demonstration/shaders/fragment/refraction.frag" target="_blank" rel="noopener noreferrer">refraction.frag</a></li>
<li><a href="https://github.com/lettier/3d-game-shaders-for-beginners/blob/master/demonstration/shaders/fragment/base-combine.frag" target="_blank" rel="noopener noreferrer">base-combine.frag</a></li>
</ul>
<h2 id="copyright">Copyright</h2>
<p>(C) 2019 David Lettier <br> <a href="https://www.lettier.com">lettier.com</a></p>
<p><a href="screen-space-reflection.html"><span class="emoji" data-emoji="arrow_backward">◀️</span></a> <a href="index.html"><span class="emoji" data-emoji="arrow_double_up">⏫</span></a> <a href="#"><span class="emoji" data-emoji="arrow_up_small">🔼</span></a> <a href="#copyright"><span class="emoji" data-emoji="arrow_down_small">🔽</span></a> <a href="foam.html"><span class="emoji" data-emoji="arrow_forward">▶️</span></a></p>
  </body>
</html>
